package utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import eduni.simjava.Sim_entity;
import eduni.simjava.Sim_event;
import eduni.simjava.Sim_from_p;
import eduni.simjava.Sim_port;
import eduni.simjava.Sim_predicate;
import eduni.simjava.Sim_system;

public class TimeResponseMonitor extends Sim_entity {

	private Sim_port fromSpooler;
	private Map<String, Double> data = new HashMap<String, Double>();
	private Map<String, Integer> printers = new HashMap<String, Integer>();
	private Map<String, List<Integer>> printersHistory = new HashMap<String, List<Integer>>();

	public TimeResponseMonitor(String name) {
		super(name);

		this.fromSpooler = new Sim_port("fromSpooler");
		this.add_port(fromSpooler);
	}

	public Map<String, List<Integer>> getPrintersHistory() {
		for (String printer : printers.keySet()) {
			printersHistory.get(printer).add(printers.get(printer));
		}
		return printersHistory;
	}

	public Map<String, Double> getData() {
		return data;
	}

	@Override
	public void body() {

		for (String printer : printers.keySet()) {
			if (printersHistory.get(printer) == null) {
				List<Integer> list = new ArrayList<Integer>();
				printersHistory.put(printer, list);
			}
			printersHistory.get(printer).add(printers.get(printer));
		}
		
		data.clear();
		printers.clear();

		while (Sim_system.running()) {
			// Captura apenas os eventos vindos do spooler
			Sim_event e = new Sim_event();
			Sim_predicate predicate = new Sim_from_p(
					Sim_system.get_entity_id("spooler"));
			this.sim_get_next(predicate, e);

			// Quando vai realizar o calculo das metricas o simjava manda uns
			// eventos com id -1 e acaba quebrando a aplicação.
			if (e.get_tag() < 0
					|| e.get_tag() != PackageType.RESPONSE.ordinal()) {
				this.sim_completed(e);
				return;
			}

			// Extrai o pacote recebido
			LANPackage lanPackage = (LANPackage) e.get_data();

			// Faz medições gerais e especifica de cada classe de requisições.
			CalcMetrics("GENERAL_", lanPackage);
			if (lanPackage.isPriority()) {
				CalcMetrics("PRIORITY_", lanPackage);
			} else {
				CalcMetrics("NORMAL_", lanPackage);
			}

			// Adiciona a contagem de impressoes por empressora
			String printer = lanPackage.get("PRINTER").toString();
			if (printers.get(printer) == null) {
				printers.put(printer, 1);
			} else {
				printers.put(printer, printers.get(printer) + 1);
			}
		}
	}

	/**
	 * Calcula a média do tempo de resposta e captura os valores mínimo e máximo
	 * 
	 * @param prefix
	 * @param lanPackage
	 */
	private void CalcMetrics(String prefix, LANPackage lanPackage) {
		double time = Sim_system.clock();
		Double latency = time - (Double) lanPackage.get("INIT");

		Double avarage;
		if (data.get(prefix + "AVARAGE_RESPONSE_TIME") == null) {
			avarage = latency;
		} else {
			avarage = ((Double) data.get(prefix + "AVARAGE_RESPONSE_TIME") + latency) / 2;
		}
		data.put(prefix + "AVARAGE_RESPONSE_TIME", avarage);

		// Verifica maior e menor
		if (data.get(prefix + "MIN") == null
				|| latency < (Double) data.get(prefix + "MIN")) {
			data.put(prefix + "MIN", latency);
		}
		if (data.get(prefix + "MAX") == null
				|| latency > (Double) data.get(prefix + "MAX")) {
			data.put(prefix + "MAX", latency);
		}
	}
}
